/*
 * Circuit3D.cpp
 *
 *  Created on: Apr 5, 2012
 *      Author: hxu
 */

#include "Circuit3D.h"
#include "Toolset.h"

using namespace std;
using namespace Txuh;

double BUFR; // output r of buffers, ohm
double BUFC; // input c of buffers, F
double BUFD; // intrinsic delay of buffers, s
double WIRER; // r of wires, ohm/0.1um, the unit of the circuit
double WIREC; // c of wires, F/0.1um
double TSVR; // r of TSVs, ohm/tier
double TSVC; // c of TSVs, F/tier
double BUFLOAD; // Cmax a buffer can drive

namespace Cxuh {

Circuit3D::Circuit3D(std::ifstream& ifile, std::fstream& ofile) :
		ofs(ofile), lstage(0), srcz(0), root(NULL), num_buf(0), num_tsv(
				0), num_sink(0), num_mp(0) {
	extractPar(ifile);
	extract_info(path_info, lstage, srcz);
}

void Circuit3D::extractPar(std::ifstream& fs){
	// extract parameters from file stream
	string linestr;
	string var = "";

	while (fs.eof() == 0) {
		// parse each line
		getline(fs, linestr);
		parseLine(linestr);
	}
}



void Circuit3D::parseLine(string& strline){
	// parse a line

	if(strline.empty())
		return;

	istringstream sstr(strline);
	string str1, str2;
	sstr >> str1; // the 1st string is the name
	sstr >> str2;

	// translate parameters
	if (str1 == "IFILENAME") {
		IFILENAME = str2;
	} else if (str1 == "TREENODECNT") {
		int a = str2int(str2);
		TREENODECNT = a;
	} else if (str1 == "TSVBOUND") {
		int a = str2int(str2);
		TSVBOUND = a;
	} else if (str1 == "SRCZ") {
		int a = str2int(str2);
		srcz = a;
	} else if (str1 == "BUFR") {
		double a = str2double(str2);
		BUFR = a;
		ofs _(BUFR);
	} else if (str1 == "BUFC") {
		double a = str2double(str2);
		BUFC = a;
		ofs _(BUFC);
	} else if (str1 == "BUFD") {
		double a = str2double(str2);
		BUFD = a;
		ofs _(BUFD);
	} else if (str1 == "WIRER") {
		double a = str2double(str2);
		WIRER = a;
		ofs _(WIRER);
	} else if (str1 == "WIREC") {
		double a = str2double(str2);
		WIREC = a;
		ofs _(WIREC);
	} else if (str1 == "TSVR") {
		double a = str2double(str2);
		TSVR = a;
		ofs _(TSVR);
	} else if (str1 == "TSVC") {
		double a = str2double(str2);
		TSVC = a;
		ofs _(TSVC);
	} else if (str1 == "BUFLOAD") {
		double a = str2double(str2);
		BUFLOAD = a;
		ofs _(BUFLOAD);
	} else if (str1.find("path")==0){
		// path information
		int id = str2int(str1.substr(str1.rfind("_")+1, str1.size()-1));

		Cxuh::clockpath p1(id);

		while(true){
			if (str2 != "") {
				int pos = str2.find("_");
				int nbuf = str2int(str2.substr(0, pos + 1));
				int tier = str2int(str2.substr(pos + 1, str2.size()));

				if (nbuf <= 0 || tier <= 0) {
					string errstr = " Error in " + str1;
					throw(errstr);
				}

				p1.add_spath(nbuf, tier);
			}

			if(sstr.eof())
				break;
			else{
				str2 = ""; // remove previous info.
				sstr >> str2;
			}
		}

		path_info.push_back(p1);

		ofs << p1;
	} else if (str1 == "lstage"){
		// wire length of buffer stage
		lstage = str2int(str2);
		ofs _(lstage);
	} else if (str1.find("vnoise")==0){
		// vdd noise
		int id = str2int(str1.substr(str1.find("_") + 1, str1.size()));
		double v = str2double(str2);
		sstr >> str2;
		double f = str2double(str2);
		sstr >> str2;
		double phi = str2double(str2);

		Cxuh::Vnoise temp(v, f, phi, id);
		vnoie_set[id] = temp;
		ofs << vnoie_set[id];
	}
}

void Circuit3D::extract_info(std::vector<clockpath>& paths, int lstage, int z){
	// Auto-generated constructor stub
	vector<clockpath>::iterator it;
	BtreeNode *node = NULL;
	BtreeNode *sink = NULL;

	if(paths.size() > 0){
		root = new BtreeNode(0, 'b', 0, 0, srcz);
		node = root;
	}

	// check whether there is shared path
	for(it=paths.begin();it<paths.end();it++){
		clockpath temp = *it;
		if(temp.getID() == 0){
			// there is a share path
			node = gen_buf_for_path(root, temp);
			break;
		}
	}

	for (it = paths.begin(); it < paths.end(); it++) {
		clockpath temp = *it;
		if (temp.getID() != 0) {
			// insert two diverging branches
			if (!node->getLchild()) {
				// 1st path
				BtreeNode *tnode = gen_buf_for_path(node, temp);
				sink = new BtreeNode(1, 's', tnode->getX(), tnode->getY(), tnode->getZ());
				tnode->setLchild(sink);
			}else{
				// 2nd path
				if (node->getLchild()->getType() == 't') {
					// there is already a tsv, insert the path after it
					BtreeNode *tnode = gen_buf_for_path(node->getLchild(), temp);
					sink = new BtreeNode(1, 's', tnode->getX(), tnode->getY(),
							tnode->getZ());
					tnode->setLchild(sink);
				} else{
					// no tsv, directly connects to root
					BtreeNode *tnode = gen_buf_for_path(node, temp);
					sink = new BtreeNode(1, 's', tnode->getX(), tnode->getY(),
							tnode->getZ());
					tnode->setLchild(sink);
				}
			}
		}
	}

	update(root);
}

void Circuit3D::update(BtreeNode * rt){
	// update the information
	if(rt){
		switch (rt->getType()) {
		case 'b':
			rt->setID(num_buf);
			num_buf++;
			break;
		case 't':
			rt->setID(num_tsv);
			num_tsv++;
			break;
		case 's':
			rt->setID(num_sink);
			num_sink++;
			break;
		case 'm':
			rt->setID(num_mp);
			num_mp++;
			break;
		}
		update(rt->getLchild());
		update(rt->getRchild());
	}
}

BtreeNode* Circuit3D::gen_buf_for_path(BtreeNode *rt, clockpath& p1){
	// generate buffers for path p1 rooting at rt
	BtreeNode *endnode = NULL;
	BtreeNode *temp = rt;

	if(!rt)
		throw(" Error: insert a path after null in Circuit3D::gen_buf_for_path()!");

	int last_tier;
	if(rt->getType()=='t')
		last_tier = rt->getZ2();
	else
		last_tier= rt->getZ();

	vector<subpath> sp1 = p1.get_sub_path();
	vector<subpath>::iterator it;

	for(it = sp1.begin(); it<sp1.end();it++){
		int n = (*it).get_nbuf();
		int tier = (*it).get_tier();

		for(int i=1;i<=n;i++){
			int x = temp->getX();
			int y = temp->getY();

			if (tier != last_tier) {
				// a tsv and a buffer right after this tsv are needed
				endnode = new BtreeNode(i, 't', x, y, last_tier, tier);
				temp->setChild(endnode);
				temp = endnode;
				endnode = new BtreeNode(i, 'b', x, y, tier);
				temp->setChild(endnode);
				temp = endnode;
				last_tier = tier;
			} else if(rt->getType()=='t'){
				// rt is a tsv, a buffer right after it is needed
				endnode = new BtreeNode(i, 'b', x, y, tier);
				temp->setChild(endnode);
				temp = endnode;
			}

			x = x+lstage;

			endnode = new BtreeNode(i, 'b', x, y, last_tier);
			temp->setChild(endnode);

			temp = endnode;
		}
	}

	return endnode;
}


Circuit3D::~Circuit3D() {
	// Auto-generated destructor stub
	delete_nodes(root);
}

void Circuit3D::delete_nodes(BtreeNode *tnode) {
	if (tnode != NULL) {
		delete_nodes(tnode->getLchild());
		delete_nodes(tnode->getRchild());
		delete tnode;
	}
}

ostream& operator<< (ostream& out, Circuit3D& circuit){
	if(!circuit.root)
		out << " Empty circuit." << endl;
	else{
		out << "NumSinks : " << circuit.num_sink << endl;
		out << "NumBufs : " << circuit.num_buf << endl;
		out << "NumTSVs : " << circuit.num_tsv << endl;
		out << "NumMPs : " << circuit.num_mp << endl << endl;
	}

	circuit.traverse(circuit.root, out);

	return out;
}

void Circuit3D::traverse(BtreeNode *rt, ostream& out){
	// output all nodes
	if(rt){
		out << *rt;
		traverse(rt->getLchild(), out);
		traverse(rt->getRchild(), out);
	}
}

} /* namespace Cxuh */
